package controller.editor;

import java.awt.Component;
import java.awt.Cursor;
import java.awt.Image;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.io.File;
import java.net.URL;
import java.util.Iterator;
import java.util.List;

import javax.swing.ImageIcon;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
import javax.swing.tree.TreePath;

import main.model.Attribute;
import main.model.Diagram;
import main.model.Entity;
import main.model.Hierarchy;
import main.model.Project;
import main.model.Relation;
import view.EditMenuView;
import view.editor.CanvasView;
import view.editor.EditorView;
import view.editor.Layout;

import com.mxgraph.model.mxCell;

import controller.Controller;
import controller.CreateHierarchyController;
import controller.CreateNewDiagramController;
import controller.NewEntityController;

public class EditorController extends Controller{

	private File fileToOpen;
	private Diagram currentDiagram;
	private Layout currentLayout;
	private Project project;
	private Boolean entityActive = false;
	private Entity entity = null;
	private EditorView view;
	private Component component;
    private Cursor cursor;
    private mxCell currentCell;
    private List<Relation> relations = null;
    private List<Hierarchy> hierarchies = null;
    private Boolean openDiagram = false;
    
    public EditorController(Project project){
    	if (project != null){
    		this.setProject(project);
    		if (CanvasView.getInstance().getController() == null){
    			CanvasView.getInstance().setController(this);
    		}
    		this.currentLayout = new Layout(CanvasView.getInstance(), this);
    		this.openDiagram = true;
    	} 
    	Toolkit tk = Toolkit.getDefaultToolkit();
    	URL url = EditorController.class.getResource("entidad.png");
    	final Image cursorImage = new ImageIcon(url).getImage();
    	this.cursor = tk.createCustomCursor(cursorImage, new Point(0,0),"cursorName");
    }
	
	@Override
	public void displayView() {
		if (view == null){
			view = new EditorView(this);
			this.component = view.getComponent();
		}
		view.show();
		if (this.openDiagram){
			fillLayouts();
			this.currentDiagram = this.project.getDiagramByName("Principal");
			this.view.drawDiagram(this.currentDiagram);
		}
	}
	
	private void fillLayouts(){
		Iterator<Diagram> it = this.project.getDiagrams().iterator();
		while (it.hasNext()){
			Diagram next = it.next();
			Layout layout = this.project.getLayoutByDiagram(next);
			layout.setGraphComponent(this.view.getGraphComponent());
			this.currentDiagram = next;
			for (String s : this.currentDiagram.getDiagramLeaves()) {
				this.view.addNewLeafToDiagram(s);
			}
		}
	}

	public File getFileToOpen() {
		return fileToOpen;
	}

	public void setFileToOpen(File fileToOpen) {
		this.fileToOpen = fileToOpen;
	}

	public void handleButtonEntity() {
		NewEntityController controller = new NewEntityController(this);
		controller.displayView();
	}
	
	
	public void handleButtonJerarquia() {
		CreateHierarchyController controller = new CreateHierarchyController(this);
		controller.displayView();
	}

	public void handleButtonRelation() {
		RelationCreationController controller = new RelationCreationController(getCurrentDiagram(), this);
		controller.displayView();
	}

	public void handleButtonPrint() {
		CanvasView.getInstance().print();
		System.out.println(this.getCurrentDiagram());
	}

	public void notifyNewEntity(Entity entity) {
		this.view.disableButtonArea();
		this.entityActive = true;
		this.entity = entity;
		this.project.addComponentName(entity.getName());
		view.addNewLeafToDiagram(entity.getName());
	}
	
	public void notifyNewGlobalEntity(Entity entity, List<Relation> relations, List<Hierarchy> hierarchies){
		this.view.disableButtonArea();
		this.entityActive = true;
		this.entity = entity;
		this.relations = relations;
		this.hierarchies = hierarchies;
	}
	
	public void notifyNewRelation(Relation rel) {
		view.drawRelation(rel);
		this.project.addComponentName(rel.getName());
		view.addNewLeafToDiagram(rel.getName());
	}


	public MouseListener getMouseListener() {
		return new MouseListener() {
			
			@Override
			public void mouseReleased(MouseEvent e) {
				main.model.Component component = CanvasView.getInstance().getComponent(e.getPoint());
				if (e.isPopupTrigger()){
					if (component != null && !component.isAttribute()){
						EditMenuView.getInstance().setComponent(component);
						EditMenuView.getInstance().setController(view.getController());
						EditMenuView.getInstance().show(e.getComponent(), e.getX(), e.getY());
					}
				}
			}
			
			@Override
			public void mousePressed(MouseEvent e) {
				main.model.Component component = CanvasView.getInstance().getComponent(e.getPoint());
				if (e.isPopupTrigger()){
					if (component != null && !component.isAttribute()){
						EditMenuView.getInstance().setComponent(component);
						EditMenuView.getInstance().setController(view.getController());
						EditMenuView.getInstance().show(e.getComponent(), e.getX(), e.getY());
					}
				}
			}
			
			@Override
			public void mouseExited(MouseEvent e) {

			}
			
			@Override
			public void mouseEntered(MouseEvent e) {
				if (entityActive){
					component.setCursor(cursor);
				}
			}
			
			@Override
			public void mouseClicked(MouseEvent e) {
				main.model.Component component = CanvasView.getInstance().getComponent(e.getPoint());
				if (e.getButton() == 1){
					if (component != null){
						System.out.println(component.getName());
						mxCell selectedCell = CanvasView.getInstance().getSelectedCell(e.getPoint());
						System.out.println("Punto tocado: " + e.getX() + "; " + e.getY());
						System.out.println("Punto: " + selectedCell.getGeometry().getX() + "; " + selectedCell.getGeometry().getY());
						System.out.println("Centro: " + selectedCell.getGeometry().getCenterX() + "; " + selectedCell.getGeometry().getCenterY());
						Point positionComponent = project.getPointForComponent(currentDiagram, component);
						System.out.println("Punto guardado en mapa: " + positionComponent.getX() + "; " + positionComponent.getY());
					}
					if (entityActive){
						view.drawEntity(entity, e.getPoint());
						if (relations!=null){
							for (Relation rel : relations) {
								view.drawRelation(rel);
							}
						}
						if (hierarchies != null){
							for (Hierarchy h : hierarchies){
								view.drawHierarchy(h);
							}
						}
						view.addNewLeafToDiagram(entity.getName());
						for (Hierarchy hierarchy : hierarchies) {
							view.addNewLeafToDiagram(hierarchy.getName());
						}
						for (Relation r : relations) {
							view.addNewLeafToDiagram(r.getName());
						}
						entityActive = false;
						relations = null;
						hierarchies = null;
						view.enableButtonArea();
					}					
				}
			}
		};
	}
	
	public MouseMotionListener getMouseMotionListener(){
		return new MouseMotionListener() {
			
			@Override
			public void mouseMoved(MouseEvent e) {
				if (entityActive){
					component.setCursor(cursor);
				}
				if (currentCell != null){
					CanvasView.getInstance().updateEntity(currentCell);
					currentCell = null;
				}
			}
			
			@Override
			public void mouseDragged(MouseEvent e) {
				if (currentCell == null){
					currentCell = CanvasView.getInstance().getSelectedCell(e.getPoint());
				}
			}
		};
	}

	public void updateLayout(main.model.Component component, Point p) {
		this.project.setNewPointToComponent(currentDiagram, component, p);
	}

	public void bindComponentToPoint(main.model.Component component, Point p) {
		this.project.bindComponentToPoint(currentDiagram, component, p);
	}

	public Diagram getCurrentDiagram() {
		return currentDiagram;
	}

	public void setDiagram(Diagram diagram) {
		this.currentDiagram = diagram;
	}

	public void createProjectWithCurrentName(String path) {
		this.setProject(new Project(path));
		if (CanvasView.getInstance().getController() == null){
			CanvasView.getInstance().setController(this);
		}
		Layout l = new Layout(CanvasView.getInstance(), this);
		this.getProject().addDiagram(new Diagram("Principal"), l);
		this.currentDiagram = this.getProject().getFirstDiagram();
		this.currentLayout = l;
	}

	public Project getProject() {
		return project;
	}

	public void setProject(Project project) {
		this.project = project;
	}

	public void addDiagramToProject(String diagramName) {
		Diagram newDiagram = new Diagram(diagramName);
		Layout l = new Layout(CanvasView.getInstance(), this);
		l.setGraphComponent(this.view.getGraphComponent());
		this.getProject().addDiagram(newDiagram, l);
		this.currentDiagram = newDiagram;
		this.view.addNewDiagramToTree(diagramName);
		this.view.cleanScreen();
		this.currentLayout = l;
		this.view.drawDiagram(this.currentDiagram);
	}

	public void addDiagramToProject(Diagram diagram){
		Layout l = new Layout(CanvasView.getInstance(), this);
		this.getProject().addDiagram(diagram, l);
	}
	
	public void handleButtonNuevoDiagrama() {
		CreateNewDiagramController controller = new CreateNewDiagramController(this);
		controller.displayView();
	}

	public void handleButtonSave() {
		this.project.saveDiagram(this.currentDiagram);
		JDialog dialogo = new JDialog(); 
		JOptionPane.showMessageDialog(dialogo,"Diagrama guardado", "Notificacion",
                    JOptionPane.INFORMATION_MESSAGE);
	}

	public void setNewCurrentDiagram(String diagramName) {
		this.currentDiagram = this.project.getDiagramByName(diagramName);
		this.currentLayout = this.project.getLayoutByDiagram(this.currentDiagram);
		this.view.cleanScreen();
		this.currentLayout.cleanLists();
		this.view.drawDiagram(this.currentDiagram);
	}

	public boolean existsDiagram(String selection) {
		return (this.project.getDiagramByName(selection) != null);
	}

	public Point getPointForComponent(main.model.Component c) {
		return this.getProject().getPointForComponent(this.currentDiagram, c);
	}

	public Layout getCurrentLayout() {
		return currentLayout;
	}

	public void setCurrentLayout(Layout currentLayout) {
		this.currentLayout = currentLayout;
	}

	public void handleButtonExportar() {
		try {
			ExportToJPGController.getInstance().exportScreen(this.project.getName(), this.currentDiagram.getName(), this.currentLayout.getGraphComponent());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void handleButtonSalir() {
		JDialog dialogo = new JDialog(); 
			//default icon, custom title
		int n = JOptionPane.showConfirmDialog(dialogo,
					"Guardar los cambios antes de salir?",
					"Salir", JOptionPane.YES_NO_OPTION);
		if( n == 0){
			this.handleButtonGuardarTodos();
		}
		System.exit(0);
	}

	public void deleteDialog(main.model.Component component) {
		Boolean puedeBorrar = Boolean.TRUE;
		if (component.isAttribute()){
			Boolean originalParentFound = Boolean.FALSE;
			main.model.Component current = component;
			main.model.Component parent = null;
			while (!originalParentFound){
				Attribute a = (Attribute)current;
				parent = a.getParent();
				if (parent.isEntity() || parent.isRelation()){
					originalParentFound = Boolean.TRUE;
				} else {
					current = parent;
				}
			}
			if (parent.isEntity()){
				Entity e = (Entity)parent;
				if (e.getDiagramParent().equals(this.currentDiagram.getName())){
					puedeBorrar = Boolean.TRUE;
				} else {
					puedeBorrar = Boolean.FALSE;
					JDialog dialogo = new JDialog(); 
					JOptionPane.showMessageDialog(dialogo,"No puede borrar parcialmente una entidad global no perteneciente al diagrama", "Notificacion",
			                    JOptionPane.INFORMATION_MESSAGE);
				}
			}
		} 
		if (puedeBorrar) {
			JDialog dialogo = new JDialog(); 
			//default icon, custom title
			int n = JOptionPane.showConfirmDialog(dialogo,
					"Desea Borrar el componente seleccionado?",
					"Borar", JOptionPane.YES_NO_OPTION);
			if( n == 0){
				deleteComponent(this.currentDiagram, component);
				setNewCurrentDiagram(this.currentDiagram.getName());
			}
		}
	}
	
	private void deleteComponent(Diagram diagram, main.model.Component component){
		Point pointForComponent = this.project.getPointForComponent(diagram, component);
		mxCell selectedCell = null;
		if (pointForComponent != null){
			selectedCell = CanvasView.getInstance().getSelectedCell(pointForComponent);
		}
		Boolean removeFromAllDiagrams = Boolean.FALSE;
		if (component.isEntity()){
			Entity e = (Entity)component;
			if (e.getDiagramParent().equals(this.currentDiagram.getName())){
				removeFromAllDiagrams = Boolean.TRUE;
				for (Diagram d : this.project.getDiagrams()) {
					this.view.removeComponentFromTree(d.getName(), component.getName());
				}
				this.project.removeComponentName(e.getName());
			} else {
				this.view.removeComponentFromTree(diagram.getName(), component.getName());
			}
		} else {
			this.view.removeComponentFromTree(diagram.getName(), component.getName());
		}
		if (selectedCell != null){
			deleteChildrenCells(selectedCell, component);
		}
		if (removeFromAllDiagrams){
			Iterator<Diagram> iterator = this.project.getDiagrams().iterator();
			while (iterator.hasNext()){
				Diagram next = iterator.next();
				List<Relation> associatedRelations = next.removeComponent(component);
				if (associatedRelations != null){
					Iterator<Relation> it = associatedRelations.iterator();
					while (it.hasNext()){
						deleteComponent(next, it.next());
					}
				}
				Iterator<Hierarchy> hierarchyIt = next.getHierarchiesToRemove(component).iterator();
				if (hierarchyIt.hasNext()){
					Hierarchy hNext = hierarchyIt.next();
					diagram.removeHierarchy(hNext);
					this.view.removeComponentFromTree(diagram.getName(), hNext.getName());
				}
			}
		} else {
			List<Relation> associatedRelations = diagram.removeComponent(component);
			if (associatedRelations != null){
				Iterator<Relation> it = associatedRelations.iterator();
				while (it.hasNext()){
					deleteComponent(diagram, it.next());
				}
			}
			Iterator<Hierarchy> hierarchyIt = diagram.getHierarchiesToRemove(component).iterator();
			if (hierarchyIt.hasNext()){
				Hierarchy hNext = hierarchyIt.next();
				diagram.removeHierarchy(hNext);
				this.view.removeComponentFromTree(diagram.getName(), hNext.getName());
			}
		}
	}

	public void deleteChildrenCells(mxCell cellToRemove, main.model.Component component){
		this.view.removeComponent(cellToRemove);
		Iterator<Attribute> iterator = component.getAttributes().iterator();
		while (iterator.hasNext()){
			Attribute next = iterator.next();
			Point pointForAtt = this.project.getPointForComponent(this.currentDiagram, next);
			mxCell cell = CanvasView.getInstance().getSelectedCell(pointForAtt);
			this.deleteChildrenCells(cell, next);
		}
		iterator = component.getIdAttributes().iterator();
		while (iterator.hasNext()){
			Attribute next = iterator.next();
			Point pointForAtt = this.project.getPointForComponent(this.currentDiagram, next);
			mxCell cell = CanvasView.getInstance().getSelectedCell(pointForAtt);
			this.deleteChildrenCells(cell, next);
		}
	}

	public MouseListener getTreeMouseListener() {
		return new MouseListener() {
			@Override
			public void mouseReleased(MouseEvent e) {
				if (e.isPopupTrigger()){
					TreePath path = view.getTree().getPathForLocation ( e.getX (), e.getY () );
					if (path != null){
						String nodeName = path.getLastPathComponent().toString();
						main.model.Component component = currentDiagram.getComponentByName(nodeName);
						if ( component != null){
							EditMenuView.getInstance().setComponent(component);
							EditMenuView.getInstance().setController(view.getController());
							EditMenuView.getInstance().show(e.getComponent(), e.getX(), e.getY());
						}
					}
				}
			}
			
			@Override
			public void mousePressed(MouseEvent e) {
				if (e.isPopupTrigger()){
					TreePath path = view.getTree().getPathForLocation ( e.getX (), e.getY () );
					if (path != null){
						String nodeName = path.getLastPathComponent().toString();
						main.model.Component component = currentDiagram.getComponentByName(nodeName);
						if ( component != null){
							EditMenuView.getInstance().setComponent(component);
							EditMenuView.getInstance().setController(view.getController());
							EditMenuView.getInstance().show(e.getComponent(), e.getX(), e.getY());
						}
					}
				}
			}
			
			@Override
			public void mouseExited(MouseEvent arg0) {
			}
			
			@Override
			public void mouseEntered(MouseEvent arg0) {
				// TODO Auto-generated method stub
				
			}
			
			@Override
			public void mouseClicked(MouseEvent arg0) {
				// TODO Auto-generated method stub
				
			}
		};
	}

	public void editComponent(main.model.Component component2) {
		if (component2.isEntity()){
			EntityCreationController c = new EntityCreationController(this.currentDiagram, this);
			c.editEntity((Entity)component2);
		} else if (component2.isRelation()){
			RelationCreationController c = new RelationCreationController(this.currentDiagram, this);
			c.editRelation((Relation)component2);
		} else {
  		  JDialog dialogo = new JDialog(); 
		  JOptionPane.showMessageDialog(dialogo,"No se puede editar la jerarquia", "Notificacion",
				  JOptionPane.INFORMATION_MESSAGE);
		}
	}

	public void notifyEditedEntity(Entity entity) {
		view.removeComponentFromTree(currentDiagram.getName(), entity.getName());
		view.addNewLeafToDiagram(entity.getName());
		view.cleanScreen();
		view.drawDiagram(this.currentDiagram);
	}
	
	public void notifyEditedRelation(Relation rel) {
		view.removeComponentFromTree(currentDiagram.getName(), rel.getName());
		view.addNewLeafToDiagram(rel.getName());
		view.cleanScreen();
		view.drawDiagram(this.currentDiagram);
	}

	public void notifyNewHierarchy(Hierarchy h) {
		view.drawHierarchy(h);
		view.addNewLeafToDiagram(h.getName());
	}

	public void handleButtonValidar() {
		if (CanvasView.getInstance().isValid()){
			JDialog dialogo = new JDialog(); 
			  JOptionPane.showMessageDialog(dialogo,"Diagrama valido", "Notificacion",
					  JOptionPane.INFORMATION_MESSAGE);
		} else {
			JDialog dialogo = new JDialog(); 
			  JOptionPane.showMessageDialog(dialogo,"Diagrama invalido", "Notificacion",
					  JOptionPane.INFORMATION_MESSAGE);
		}
	}

	public void removePointForComponent(main.model.Component c) {
		this.getProject().removePointForComponent(this.currentDiagram, c);
	}

	public void bindPointsToNewComponent(main.model.Component oldComponent, main.model.Component newComponent) {
		Point pointForComponent = this.getPointForComponent(oldComponent);
		removePointForComponent(oldComponent);
		bindComponentToPoint(newComponent, pointForComponent);
	}

	public void handleButtonGuardarTodos() {
		this.project.saveAll();
		JDialog dialogo = new JDialog(); 
		 JOptionPane.showMessageDialog(dialogo,"Todos los diagramas se han guardado correctamente", "Notificacion",
				  JOptionPane.INFORMATION_MESSAGE);
	}

}
